////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_CONCURRENCY_THREAD_H__
#define __WN_CONCURRENCY_THREAD_H__

#include "WNCore/inc/WNTypes.h"
#include "WNContainers/inc/WNResourceBase.h"

#ifdef _WN_WINDOWS
    #include <windows.h>
#elif defined _WN_LINUX
    #include <pthread.h>
#endif

namespace WNConcurrency {
    class WNThread {
    public:
        WN_FORCE_INLINE WNThread();
        WN_FORCE_INLINE WNThread(const WNThread& _thread);

        template <typename Return>
        explicit WN_FORCE_INLINE WNThread(Return (*_function)());

        template <typename Return, typename Parameter>
        explicit WN_FORCE_INLINE WNThread(Return (*_function)(Parameter), Parameter _parameter);

        template <typename Return, typename Class, typename Parameter>
        explicit WN_FORCE_INLINE WNThread(Return (Class::*_function)(Parameter), Parameter _parameter);

        WN_FORCE_INLINE ~WNThread();

        WN_FORCE_INLINE WNThread& operator = (const WNThread& _thread);

        WN_FORCE_INLINE WN_BOOL SetAffinity(WN_SIZE_T _affinity);
        WN_FORCE_INLINE WN_BOOL SetPriority(WN_UINT32 _priority);

        WN_FORCE_INLINE WN_SIZE_T GetAffinity() const;
        WN_FORCE_INLINE WN_UINT32 GetPriority() const;

        template <typename Return>
        WN_FORCE_INLINE WN_BOOL GetReturn(Return* _return) const;

        WN_FORCE_INLINE WN_BOOL IsValid() const;
        WN_FORCE_INLINE WN_BOOL IsReady() const;
        WN_FORCE_INLINE WN_BOOL IsRunning() const;
        WN_FORCE_INLINE WN_BOOL IsFinished() const;

        WN_FORCE_INLINE WN_BOOL Join(WN_UINT32 _milliseconds = -1) const;

    private:
        enum __WNThreadState {
            eWNInvalid,
            eWNReady,
            eWNRunning,
            eWNFinished
        };

        class __WNThreadData : public WNContainers::WNResourceBase {
        public:
            WN_FORCE_INLINE __WNThreadData() {}
            virtual WN_FORCE_INLINE ~__WNThreadData();

        public:
            WN_UINT32 mID;

            WN_UINT32 mPriority;
            WN_SIZE_T mAffinity;

            WN_VOID* mFunction;
            WN_VOID* mParameter;
            WN_VOID* mReturn;

            #ifdef _WN_LINUX
                pthread_t mThread;
            #endif

            __WNThreadState mState;
        };

        template <typename, typename>
        class __WNThreadWrapper {
        public:
            #ifdef _WN_WINDOWS
                static WN_FORCE_INLINE DWORD WINAPI Execute(LPVOID _threadData);
            #elif defined _WN_LINUX
                static WN_FORCE_INLINE void* Execute(void* _threadData);
            #endif

        private:
            __WNThreadWrapper() {}
        };

        template <typename Parameter>
        class __WNThreadWrapper<WN_VOID, Parameter> {
        public:
            #ifdef _WN_WINDOWS
                static WN_FORCE_INLINE DWORD WINAPI Execute(LPVOID _threadData);
            #elif defined _WN_LINUX
                static WN_FORCE_INLINE void* Execute(void* _threadData);
            #endif

        private:
            __WNThreadWrapper() {}
        };

    private:
        template <typename, typename>
        WN_FORCE_INLINE WN_VOID Create(WN_VOID* _function, WN_VOID* _parameter);

    private:
        #ifdef _WN_WINDOWS
            HANDLE mHandle;
        #endif

        __WNThreadData* mData;
    };

    #ifdef _WN_WINDOWS
        template <>
        WN_FORCE_INLINE DWORD WINAPI WNThread::__WNThreadWrapper<WN_VOID, WN_VOID>::Execute(LPVOID _threadData);
    #elif defined _WN_LINUX
        template <>
        WN_FORCE_INLINE void* WNThread::__WNThreadWrapper<WN_VOID, WN_VOID>::Execute(void* _threadData);
    #endif

    WN_FORCE_INLINE WN_VOID WNThreadYield();
    WN_FORCE_INLINE WN_VOID WNThreadSleep(WN_UINT32 _milliseconds);
}

#include "WNConcurrency/inc/WNThread.inl"

#endif // __WN_CONCURRENCY_THREAD_H__